import { bbox, hexGrid, pointGrid, distance, squareGrid, centroid, triangleGrid, clone, featureCollection,featureEach } from "@turf/turf";

import { collectionOf } from "@turf/invariant";

function interpolate(points, cellSize, options = {
    gridType: 'point',
    property: 'value',
    units: 'kilometers',
    weight: 1,
}) {
    // Optional parameters
    options = options || {};
    if (typeof options !== "object") throw new Error("options is invalid");
    var gridType = options.gridType;
    var property = options.property;
    var weight = options.weight;

    // validation
    if (!points) throw new Error("points is required");
    collectionOf(points, "Point", "input must contain Points");
    if (!cellSize) throw new Error("cellSize is required");
    if (weight !== undefined && typeof weight !== "number")
        throw new Error("weight must be a number");

    // default values
    property = property || "elevation";
    gridType = gridType || "square";
    weight = weight || 1;

    var box = options.bbox || bbox(points);
    var grid;
    switch (gridType) {
        case "point":
        case "points":
            grid = pointGrid(box, cellSize, options);
            break;
        case "square":
        case "squares":
            grid = squareGrid(box, cellSize, options);
            break;
        case "hex":
        case "hexes":
            grid = hexGrid(box, cellSize, options);
            break;
        case "triangle":
        case "triangles":
            grid = triangleGrid(box, cellSize, options);
            break;
        default:
            throw new Error("invalid gridType");
    }
    var results = [];
    featureEach(grid, function (gridFeature) {
        var zw = 0;
        var sw = 0;
        // calculate the distance from each input point to the grid points
        featureEach(points, function (point) {
            var gridPoint =
                gridType === "point" ? gridFeature : centroid(gridFeature);
            var d = distance(gridPoint, point, options);
            var zValue;
            // property has priority for zValue, fallbacks to 3rd coordinate from geometry
            if (property !== undefined) zValue = point.properties[property];
            if (zValue === undefined) zValue = point.geometry.coordinates[2];
            if (zValue === undefined) throw new Error("zValue is missing");
            if (d === 0) zw = zValue;
            var w = 1.0 / Math.pow(d, weight);
            sw += w;
            zw += w * zValue;
        });
        // write interpolated value for each grid point
        var newFeature = clone(gridFeature);
        newFeature.properties[property] = zw / sw;
        results.push(newFeature);
    });
    return featureCollection(results);
}

export { interpolate };